package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.annotation.PathPatterns;
import cc.shacocloud.mirage.restful.exception.HttpRequestBindingException;
import cc.shacocloud.mirage.restful.http.MediaType;
import cc.shacocloud.mirage.restful.util.PathUtil;
import cc.shacocloud.mirage.restful.util.RoutingContextUtils;
import cc.shacocloud.mirage.utils.FutureUtils;
import cc.shacocloud.mirage.utils.LogFormatUtils;
import cc.shacocloud.mirage.utils.PathMatcher;
import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.collection.CollUtil;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 请求响应处理器
 * <p>
 * 为整个请求响应出入口的处理器类
 */
@Slf4j
public class HandlerExecutionChain {
    
    public static final Buffer EMPTY = Buffer.buffer(Unpooled.EMPTY_BUFFER);
    private final VertxInvokeHandler invokeHandler;
    /**
     * 异常解析组合处理程序
     */
    @Setter
    @Nullable
    protected HandlerExceptionResolverComposite exceptionResolverComposite;
    @Nullable
    private Set<MediaType> mediaTypes;
    /**
     * 当前接口匹配的拦截器，如果接口路径模式为正则模式当前参数为空，使用 {@link #interceptorMappingInfos}
     */
    @Nullable
    private List<HandlerInterceptor> interceptors;
    /**
     * 当接口模式为正则模式时，该属性生效，在运行时间进行接口拦截器匹配
     */
    @Nullable
    private List<InterceptorMappingInfo> interceptorMappingInfos;
    /**
     * 路径匹配器
     */
    @Setter
    private PathMatcher pathMatcher = PathUtil.DEFAULT_PATH_MATCHER;
    
    public HandlerExecutionChain(VertxInvokeHandler invokeHandler,
                                 RequestMappingInfo requestMapping,
                                 @Nullable HandlerInterceptorComposite interceptorComposite) {
        this.invokeHandler = invokeHandler;
        
        if (interceptorComposite != null) {
            // 如果是正则模式在运行时匹配拦截器
            if (PathPatterns.REGEX.equals(requestMapping.getPathPatterns())) {
                this.interceptorMappingInfos = interceptorComposite.getInterceptorMappings();
            } else {
                this.interceptors = interceptorComposite.match(requestMapping.getPaths());
            }
        }
        
        String[] produces = requestMapping.getProduces();
        if (ArrayUtil.isNotEmpty(produces)) {
            this.mediaTypes = new HashSet<>(MediaType.parseMediaTypes(Arrays.asList(produces)));
        }
    }
    
    /**
     * 处理请求，请求的入口方法
     *
     * @param ctx 请求上下文
     */
    public void handle(io.vertx.ext.web.RoutingContext ctx) {
        final RoutingContext routingContext = createVertXRoutingContext(ctx);
        routingContext.put(RoutingContext.RECEIVE_REQUEST_TIME, System.currentTimeMillis());
        
        if (log.isDebugEnabled()) {
            log.debug("路由器收到待处理请求：{} {}", routingContext.request().method(), routingContext.normalizedPath());
            log.debug("当前请求实际绑定的处理器方法：" + this.invokeHandler);
        }
        
        if (CollUtil.isNotEmpty(mediaTypes)) {
            routingContext.put(VertxInvokeHandler.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
        }
        
        AtomicInteger interceptorIndex = new AtomicInteger(0);
        
        // 注册响应释放后的处理
        ctx.addEndHandler(res -> {
            
            if (log.isDebugEnabled()) {
                long receiveRequestTime = routingContext.get(RoutingContext.RECEIVE_REQUEST_TIME, System::currentTimeMillis);
                long handleTime = System.currentTimeMillis() - receiveRequestTime;
                log.debug(routingContext.normalizedPath() + " 请求处理完成，耗时：" + handleTime + "ms");
            }
            
            triggerAfterCompletion(routingContext, interceptorIndex, res.cause());
        });
        
        try {
            // 执行拦截器的 preHandle 方法
            applyPreHandle(routingContext, interceptorIndex)
                    .compose(goOn -> {
                        if (goOn) {   // 如果拦截器返回true则继续调用具体方法
                            return invokeHandler.invokeAndHandleRoutingContext(routingContext);
                        }
                        return Future.succeededFuture();
                    })
                    .onFailure(cause -> processExceptionHandler(routingContext, cause))
                    .onSuccess(res -> processResult(routingContext, res));
        } catch (Exception e) {
            processExceptionHandler(routingContext, e);
        }
    }
    
    /**
     * 委派给 {@link #processExceptionHandler} 方法执行，用于{@link io.vertx.ext.web.Route#failureHandler(Handler)}方法使用
     */
    void processException(io.vertx.ext.web.RoutingContext ctx) {
        RoutingContext routingContext = createVertXRoutingContext(ctx);
        processExceptionHandler(routingContext, ctx.failure());
    }
    
    /**
     * 处理异常情况
     */
    void processExceptionHandler(@NotNull RoutingContext context, Throwable cause) {
        final HttpRequest request = context.request();
        final HttpResponse response = context.response();
        
        if (log.isDebugEnabled()) {
            log.debug("请求 '" + request.context().normalizedPath() + "' 处理发生例外，进行异常解析处理逻辑...");
        }
        
        Future<Object> resFuture;
        if (this.exceptionResolverComposite != null) {
            resFuture = this.exceptionResolverComposite.resolveException(request, response, this.invokeHandler, cause)
                    .compose(res -> {
                        if (res == HandlerExceptionResolver.SKIP)
                            return Future.failedFuture(new HttpRequestBindingException("找不到对应的异常处理程序，无法处理当前异常信息: ", cause));
                        return Future.succeededFuture(res);
                    });
        } else {
            resFuture = Future.failedFuture(new HttpRequestBindingException("未设置异常处理程序，无法处理当前异常信息: ", cause));
        }
        
        resFuture.onComplete(ar -> {
            if (ar.failed()) {
                exceptionThatCannotBeResolved(response, ar.cause());
            } else {
                Object result = ar.result();
                if (result != HandlerExceptionResolver.FINISH) {
                    processResult(context, result);
                }
            }
        });
    }
    
    /**
     * 处理结果
     *
     * @see VertxInvokeHandler#resultHandle(Object, HttpResponse)
     */
    void processResult(@NotNull RoutingContext context, Object result) {
        final HttpResponse response = context.response();
        
        if (!response.ended()) {
            Future<Buffer> resultFuture;
            if (!(result instanceof Buffer)) {
                resultFuture = this.invokeHandler.resultHandle(result, response);
            } else {
                resultFuture = Future.succeededFuture((Buffer) result);
            }
            
            resultFuture.onComplete(res -> {
                if (res.succeeded()) {
                    
                    Buffer resResult = res.result();
                    
                    if (!response.ended()) {
                        if (resResult == null) resResult = EMPTY;
                        response.end(resResult);
                    } else {
                        if (log.isWarnEnabled() && resResult != null) {
                            log.warn("响应已经被关闭，丢弃结果： [" + LogFormatUtils.formatValue(result, true) + "]");
                        }
                    }
                } else {
                    processExceptionHandler(context, res.cause());
                }
            });
        } else {
            if (log.isWarnEnabled() && result != null) {
                log.warn("响应已经被关闭，丢弃结果： [" + LogFormatUtils.formatValue(result, true) + "]");
            }
        }
    }
    
    /**
     * 无法解决的异常
     */
    protected void exceptionThatCannotBeResolved(@NotNull HttpResponse response, Throwable cause) {
        // 如果当前状态码还是成功状态码则改为 500
        int statusCode = response.getStatusCode();
        if (200 <= statusCode && statusCode <= 300) {
            HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR;
            
        }
        
        if (log.isErrorEnabled()) {
            log.error("无法解决的异常信息：", cause);
        }
        response.end();
    }
    
    /**
     * 执行请求的预处理方法，如果当前请求存在映射的拦截器则优先使用，如果没有则尝试匹配拦截器映射信息
     */
    Future<Boolean> applyPreHandle(RoutingContext context,
                                   AtomicInteger interceptorIndex) {
        // 优先执行匹配好的拦截器
        if (CollUtil.isNotEmpty(this.interceptors)) {
            return applyPreHandle(context, interceptorIndex, this.interceptors);
        }
        // 尝试匹配拦截器映射信息
        else if (CollUtil.isNotEmpty(this.interceptorMappingInfos)) {
            // 匹配当前路径的拦截器
            List<HandlerInterceptor> matchInterceptorList = PathUtil.match(this.interceptorMappingInfos,
                    this.pathMatcher, context.normalizedPath());
            if (CollUtil.isNotEmpty(matchInterceptorList)) {
                return applyPreHandle(context, interceptorIndex, matchInterceptorList);
            }
        }
        
        return Future.succeededFuture(true);
    }
    
    /**
     * 执行匹配上的拦截器的 preHandle 方法
     */
    Future<Boolean> applyPreHandle(@NotNull RoutingContext context,
                                   AtomicInteger interceptorIndex,
                                   List<HandlerInterceptor> interceptors) {
        // 将当前请求匹配的拦截器挂载在请求上下文上
        context.put(RoutingContext.REQUEST_INTERCEPTORS, interceptors);
        
        final HttpRequest request = context.request();
        final HttpResponse response = context.response();
        
        return FutureUtils.sequential(interceptors,
                (interceptor, termination) -> {
                    // 拦截器前置处理
                    return interceptor.preHandle(request, response, this.invokeHandler)
                            .onSuccess(b -> termination.set(!b));
                },
                interceptorIndex,
                true);
    }
    
    
    /**
     * 在当前响应完全写入网络时调用
     */
    void triggerAfterCompletion(@NotNull RoutingContext context,
                                @NotNull AtomicInteger interceptorIndex,
                                @Nullable Throwable cause) {
        List<HandlerInterceptor> interceptors = context.get(RoutingContext.REQUEST_INTERCEPTORS);
        if (CollUtil.isNotEmpty(interceptors) && interceptorIndex.get() != 0) {
            
            // 减少1，因为自增从0开始，否者将导致执行未执行的拦截器
            interceptorIndex.decrementAndGet();
            
            final HttpRequest request = context.request();
            final HttpResponse response = context.response();
            
            // 逆序执行拦截器 afterCompletion
            FutureUtils.sequential(interceptors,
                    (interceptor, termination) -> {
                        // 处理拦截器后置方法
                        return interceptor.afterCompletion(request, response, this.invokeHandler, cause)
                                .recover(error -> {
                                    if (log.isWarnEnabled()) {
                                        log.warn("执行拦截器 afterCompletion 抛出例外！", error);
                                    }
                                    return Future.succeededFuture();
                                });
                    },
                    interceptorIndex,
                    false);
        }
    }
    
    protected RoutingContext createVertXRoutingContext(io.vertx.ext.web.RoutingContext ctx) {
        return RoutingContextUtils.createVertXRoutingContext(ctx);
    }
    
}

